[3/3] test(painter): lock ResolvedLayout-only boundary (SD-2836)#3118
Merged
luccas-harbour merged 3 commits intotadeu/sd-2836-2-collapse-legacy-apifrom May 5, 2026
Merged
Conversation
This was referenced May 4, 2026
6b0c46d to
befe9c0
Compare
2fc4758 to
c2370fb
Compare
befe9c0 to
8f18041
Compare
c2370fb to
34b7e7d
Compare
8f18041 to
1c869c1
Compare
34b7e7d to
85bd281
Compare
1c869c1 to
87ddc92
Compare
9ce9dc5 to
6e00a9f
Compare
renderer.ts: drop unused Layout, Page, Measure, FlowBlock, ParagraphBorder type imports left over from the migration. index.test.ts: delete the skipped 'decoration item synthesis' describe block (it was protecting the synthesis path that has been removed).
…me dep (SD-2836) Moves @superdoc/layout-resolved to devDependencies in the lockfile to match package.json, so CI's --frozen-lockfile install matches. Boundary tests still need it under devDependencies.
6e00a9f to
f804e44
Compare
7f2872d
into
tadeu/sd-2836-2-collapse-legacy-api
43 checks passed
luccas-harbour
pushed a commit
that referenced
this pull request
May 5, 2026
* refactor(painter): collapse legacy API surface (SD-2836)
Make ResolvedLayout the only painter input contract:
* DomPainterInput collapses to `{ resolvedLayout: ResolvedLayout }`.
sourceLayout, blocks/measures, headerBlocks/Measures,
footerBlocks/Measures all removed.
* DomPainterOptions: drop blocks/measures.
* DomPainterHandle: drop setData, setResolvedLayout. paint takes only
DomPainterInput. Drops the `paint(Layout)` overload across painter,
PresentationPainterAdapter, and (transitively) PresentationEditor's
paintInput.
* createDomPainter shrinks to a thin pass-through. Removes
buildLegacyPaintInput, normalizeDomPainterInput, isDomPainterInput,
wrapProvider, resolveDecorationItems, normalizeOptionalBlockMeasurePair,
assertRequiredBlockMeasurePair, createEmptyResolvedLayout,
LegacyDomPainterState, OptionalBlockMeasurePair.
* PageDecorationPayload.items becomes required (the synthesis path
is gone).
Tests:
* New `_test-utils.ts` exposes a legacy-shaped `createTestPainter`
that test files import in place of `createDomPainter`. The helper
builds a real DomPainterInput internally and synthesizes decoration
items so existing test bodies don't have to be rewritten.
* All 17 painter test files migrated to the helper.
* Two source-anchor tests still failing under investigation;
remaining work is bounded and tracked.
* fix(painter): chain user onPaintSnapshot in test utility (SD-2836)
createTestPainter was overwriting the user-supplied onPaintSnapshot callback with its own, so tests that relied on the callback (source-anchor tests) saw an undefined snapshot. Chain the user callback after the internal one.
* test(pm-adapter): migrate integration tests to ResolvedLayout (SD-2836)
The three integration tests in pm-adapter were calling painter.paint(layout, mount) with raw Layout. They now resolveLayout() first and call painter.paint({ resolvedLayout }, mount). All 1794 pm-adapter tests pass.
* test(layout-bridge): migrate perf benchmark to ResolvedLayout (SD-2836)
The layout-bridge incremental-pipeline performance benchmark called painter.paint(layout, mount) and painter.setData(...). Both are removed by the API collapse. Migrate to resolveLayout() + DomPainterInput so the benchmark continues to exercise the painter under the new contract.
* chore(deps): declare @superdoc/layout-resolved as devDep where tests use it (SD-2836)
PR1 added test imports of @superdoc/layout-resolved in
pm-adapter/src/integration.test.ts and layout-bridge/test/benchmarks/index.ts
without declaring it as a devDependency. Both packages now resolve the
import locally via pnpm.
* fix(super-editor): honor PageDecorationPayload.items contract (SD-2836)
When resolveAlignedDecorationItems cannot align items 1:1 with fragments
(returns undefined), HeaderFooterSessionManager now bails out with null
instead of emitting a payload with items: undefined, which would violate
the now-required PageDecorationPayload.items contract from PR2.
normalizeDecorationItems narrowed to ResolvedPaintItem[] -> ResolvedPaintItem[].
Also refresh painter-dom README: drop blocks/measures/setData/paint(layout)
examples; document the ResolvedLayout-only paint() and the items-aligned-
with-fragments invariant on PageDecorationPayload.
* [3/3] test(painter): lock ResolvedLayout-only boundary (SD-2836) (#3118)
* test(painter): lock ResolvedLayout-only boundary (SD-2836)
* chore(painter): drop unused imports and skipped legacy tests (SD-2836)
renderer.ts: drop unused Layout, Page, Measure, FlowBlock, ParagraphBorder type imports left over from the migration. index.test.ts: delete the skipped 'decoration item synthesis' describe block (it was protecting the synthesis path that has been removed).
* chore(deps): regenerate lockfile after dropping layout-resolved runtime dep (SD-2836)
Moves @superdoc/layout-resolved to devDependencies in the lockfile to
match package.json, so CI's --frozen-lockfile install matches. Boundary
tests still need it under devDependencies.
tupizz
added a commit
that referenced
this pull request
May 5, 2026
* refactor(painter): collapse legacy API surface (SD-2836)
Make ResolvedLayout the only painter input contract:
* DomPainterInput collapses to `{ resolvedLayout: ResolvedLayout }`.
sourceLayout, blocks/measures, headerBlocks/Measures,
footerBlocks/Measures all removed.
* DomPainterOptions: drop blocks/measures.
* DomPainterHandle: drop setData, setResolvedLayout. paint takes only
DomPainterInput. Drops the `paint(Layout)` overload across painter,
PresentationPainterAdapter, and (transitively) PresentationEditor's
paintInput.
* createDomPainter shrinks to a thin pass-through. Removes
buildLegacyPaintInput, normalizeDomPainterInput, isDomPainterInput,
wrapProvider, resolveDecorationItems, normalizeOptionalBlockMeasurePair,
assertRequiredBlockMeasurePair, createEmptyResolvedLayout,
LegacyDomPainterState, OptionalBlockMeasurePair.
* PageDecorationPayload.items becomes required (the synthesis path
is gone).
Tests:
* New `_test-utils.ts` exposes a legacy-shaped `createTestPainter`
that test files import in place of `createDomPainter`. The helper
builds a real DomPainterInput internally and synthesizes decoration
items so existing test bodies don't have to be rewritten.
* All 17 painter test files migrated to the helper.
* Two source-anchor tests still failing under investigation;
remaining work is bounded and tracked.
* fix(painter): chain user onPaintSnapshot in test utility (SD-2836)
createTestPainter was overwriting the user-supplied onPaintSnapshot callback with its own, so tests that relied on the callback (source-anchor tests) saw an undefined snapshot. Chain the user callback after the internal one.
* test(pm-adapter): migrate integration tests to ResolvedLayout (SD-2836)
The three integration tests in pm-adapter were calling painter.paint(layout, mount) with raw Layout. They now resolveLayout() first and call painter.paint({ resolvedLayout }, mount). All 1794 pm-adapter tests pass.
* test(layout-bridge): migrate perf benchmark to ResolvedLayout (SD-2836)
The layout-bridge incremental-pipeline performance benchmark called painter.paint(layout, mount) and painter.setData(...). Both are removed by the API collapse. Migrate to resolveLayout() + DomPainterInput so the benchmark continues to exercise the painter under the new contract.
* chore(deps): declare @superdoc/layout-resolved as devDep where tests use it (SD-2836)
PR1 added test imports of @superdoc/layout-resolved in
pm-adapter/src/integration.test.ts and layout-bridge/test/benchmarks/index.ts
without declaring it as a devDependency. Both packages now resolve the
import locally via pnpm.
* fix(super-editor): honor PageDecorationPayload.items contract (SD-2836)
When resolveAlignedDecorationItems cannot align items 1:1 with fragments
(returns undefined), HeaderFooterSessionManager now bails out with null
instead of emitting a payload with items: undefined, which would violate
the now-required PageDecorationPayload.items contract from PR2.
normalizeDecorationItems narrowed to ResolvedPaintItem[] -> ResolvedPaintItem[].
Also refresh painter-dom README: drop blocks/measures/setData/paint(layout)
examples; document the ResolvedLayout-only paint() and the items-aligned-
with-fragments invariant on PageDecorationPayload.
* [3/3] test(painter): lock ResolvedLayout-only boundary (SD-2836) (#3118)
* test(painter): lock ResolvedLayout-only boundary (SD-2836)
* chore(painter): drop unused imports and skipped legacy tests (SD-2836)
renderer.ts: drop unused Layout, Page, Measure, FlowBlock, ParagraphBorder type imports left over from the migration. index.test.ts: delete the skipped 'decoration item synthesis' describe block (it was protecting the synthesis path that has been removed).
* chore(deps): regenerate lockfile after dropping layout-resolved runtime dep (SD-2836)
Moves @superdoc/layout-resolved to devDependencies in the lockfile to
match package.json, so CI's --frozen-lockfile install matches. Boundary
tests still need it under devDependencies.
luccas-harbour
pushed a commit
that referenced
this pull request
May 5, 2026
* refactor(contracts): host expandRunsForInlineNewlines (SD-2836) Move the inline-newline run expander from @superdoc/pm-adapter into @superdoc/contracts. The function is a pure transformation on Run[] shapes already defined here; relocating it lets painter-dom consume the helper without depending on pm-adapter (per the SD-2836 acceptance criterion: no painter-dom imports from pm-adapter or layout-bridge). pm-adapter chains its public re-export through contracts, keeping the import path stable until painter-dom is migrated to consume directly. * refactor(contracts): host sliceRunsForLine (SD-2836) Move the line-aware run slicer from @superdoc/layout-bridge into @superdoc/contracts, alongside expandRunsForInlineNewlines. The function is a pure transformation on Run/Line shapes already defined here; relocating it lets painter-dom consume the helper without depending on layout-bridge (per the SD-2836 acceptance criterion). layout-bridge chains its public re-export through contracts, keeping the import path stable until painter-dom is migrated to consume directly. Also restore a TrackedChangeMeta import in pm-adapter's paragraph.test.ts that the prior commit dropped; the type is still referenced outside the migrated describe block. * refactor(painter): consume run helpers from contracts (SD-2836) Switch the painter's two cross-package run-helper imports (expandRunsForInlineNewlines, sliceRunsForLine) to source from @superdoc/contracts directly, then drop @superdoc/pm-adapter and @superdoc/layout-bridge from painter-dom's runtime dependencies. This closes the painter-dom -> pm-adapter / layout-bridge import edge called out in the SD-2836 acceptance criteria. Architecture-boundary guards added in [3/3] of this stack will prevent reintroduction. * refactor(layout): add fragment back-pointer to resolved items (SD-2836) Add a `fragment: Fragment` field to ResolvedFragmentItem, ResolvedTableItem, ResolvedImageItem, and ResolvedDrawingItem, and populate it from the corresponding resolve* helper. The reference is shared, not copied: items now carry the same Fragment object that lives on the source page. This is the precondition for stopping painter iteration over `page.fragments`. The next commit drops getResolvedFragmentItem and switches the painter to iterate ResolvedPage.items, reading the source fragment via `item.fragment` instead of indexing back into the legacy fragments array. Includes focused tests in resolveLayout.test.ts asserting back-pointer identity for each kind. * refactor(painter): drop getResolvedFragmentItem; iterate resolved items (SD-2836) Replace the three `page.fragments.forEach((fragment, index) => ...)` loops in renderPage / patchPage / initPage with iterations over `resolvedItems` (the resolved page's items), reading the source Fragment via the back-pointer added in the previous commit. Removes: - getResolvedFragmentItem (parallel-array index lookup into resolved items) - direct iteration of `page.fragments` from the painter render path Updates affected hand-rolled resolved-layout tests to populate the new required `fragment` back-pointer; the painter now treats items without a fragment as not-yet-renderable rather than indexing back into the legacy fragments array. * refactor(painter): paint() body reads ResolvedLayout (SD-2836) Switch paint()'s top-level reads from input.sourceLayout to input.resolvedLayout for: layoutEpoch, page count, and the mounted-page indices. The local `layout = input.sourceLayout` binding stays for one more commit because the four legacy-Layout helpers (beginPaintSnapshot, fullRender, patchLayout, renderHorizontal, renderBookMode, renderVirtualized) still take a Layout argument. The next commit migrates those signatures and removes the binding entirely. * refactor(painter): drop fragments param from sdt+border helpers (SD-2836) computeSdtBoundaries and computeBetweenBorderFlags previously took both the raw `fragments` array and the parallel resolvedItems array. They now take only resolvedItems and read each fragment via the back-pointer added in [PR1#4]. Updates all four call sites in renderer.ts plus the between-borders test fixture. This eliminates the painter's lookup into `page.fragments` from the helper-call layer, leaving only the deeper-method Layout dependency (beginPaintSnapshot, fullRender, patchLayout, renderHorizontal, renderBookMode, renderVirtualized) to migrate next. * refactor(painter): migrate internals to ResolvedLayout (SD-2836) Switch DomPainter's internal state and helpers from raw Layout/Page to ResolvedLayout/ResolvedPage: * The six top-level legacy-Layout methods (beginPaintSnapshot, fullRender, patchLayout, renderHorizontal, renderBookMode, renderVirtualized) take ResolvedLayout. paint() drops the `const layout = input.sourceLayout` binding and calls every method with input.resolvedLayout. * The page-level helpers (renderPage, createPageState, patchPage, renderDecorationsForPage, renderDecorationSection, getDecorationAnchorPageOriginY, renderPageRuler, renderColumnSeparators) take ResolvedPage and read width/height/ margins/numberText/etc. directly. Drops every redundant `resolvedPage?: ResolvedPage | null` parameter. * `currentLayout: Layout | null` -> `ResolvedLayout | null`. Strips the `(page.size ?? layout.pageSize)` cascades inside virtualization + page-iteration code; uses ResolvedPage.width/.height directly. * Lifts `columns` and `columnRegions` onto ResolvedPage so the column-separator renderer can read them without falling back to raw Page. Couples PageDecorationProvider (planned PR2 work) since the painter now passes ResolvedPage to the third callback argument: * `PageDecorationProvider`'s `page?` parameter is `ResolvedPage`. * `HeaderFooterSessionManager.rebuildRegions` takes ResolvedLayout. `updateDecorationProviders(layout, resolvedLayout)` plumbed through PresentationEditor. * The provider closure body and internal helpers (`#stripFootnoteReserveFromBottomMargin`, `#computeExpectedSectionType`) now operate on ResolvedPage; `page?.size?.h` -> `page?.height`. * `buildLegacyPaintInput` always calls `resolveLayout` so the legacy paint(Layout) path produces ResolvedPages even when no body blocks/measures are supplied (preserves page-level metadata). Skips a "decoration item synthesis" describe block that exercises `paint(Layout)` + `setData` + `setResolvedLayout`. Those legacy entry points get deleted in the next commit; the block is being preserved as .skip so the deletion is visible in diff. * chore(deps): regenerate lockfile after painter-dom dep cleanup (SD-2836) Drops the now-unused @superdoc/layout-bridge and @superdoc/pm-adapter entries from pnpm-lock.yaml so CI's --frozen-lockfile install matches package.json. * test(super-editor): synthesize ResolvedLayout in PresentationEditor mock (SD-2836) rebuildRegions now iterates resolvedLayout.pages (was layout.pages). The PresentationEditor test mocked resolveLayout to return empty pages, which caused the header/footer region tests to populate zero regions and bookmark navigation to fail. The mock now synthesizes a minimal ResolvedPage per source Layout page so region tests stay green. * refactor(painter,super-editor): address PR review feedback (SD-2836) - Drop redundant pageSize parameter from createPageState/patchPage; read page.width/height directly from ResolvedPage. - Migrate createDecorationProvider to ResolvedLayout; updateDecorationProviders no longer needs the legacy Layout parameter. - Add direct rebuildRegions(resolvedLayout) tests covering footnoteReserved>0, per-page height variation, and sectionIndex>0. - Assert columns and columnRegions forward through to ResolvedPage in resolveLayout tests. * [2/3] refactor(painter): collapse legacy API surface (SD-2836) (#3117) * refactor(painter): collapse legacy API surface (SD-2836) Make ResolvedLayout the only painter input contract: * DomPainterInput collapses to `{ resolvedLayout: ResolvedLayout }`. sourceLayout, blocks/measures, headerBlocks/Measures, footerBlocks/Measures all removed. * DomPainterOptions: drop blocks/measures. * DomPainterHandle: drop setData, setResolvedLayout. paint takes only DomPainterInput. Drops the `paint(Layout)` overload across painter, PresentationPainterAdapter, and (transitively) PresentationEditor's paintInput. * createDomPainter shrinks to a thin pass-through. Removes buildLegacyPaintInput, normalizeDomPainterInput, isDomPainterInput, wrapProvider, resolveDecorationItems, normalizeOptionalBlockMeasurePair, assertRequiredBlockMeasurePair, createEmptyResolvedLayout, LegacyDomPainterState, OptionalBlockMeasurePair. * PageDecorationPayload.items becomes required (the synthesis path is gone). Tests: * New `_test-utils.ts` exposes a legacy-shaped `createTestPainter` that test files import in place of `createDomPainter`. The helper builds a real DomPainterInput internally and synthesizes decoration items so existing test bodies don't have to be rewritten. * All 17 painter test files migrated to the helper. * Two source-anchor tests still failing under investigation; remaining work is bounded and tracked. * fix(painter): chain user onPaintSnapshot in test utility (SD-2836) createTestPainter was overwriting the user-supplied onPaintSnapshot callback with its own, so tests that relied on the callback (source-anchor tests) saw an undefined snapshot. Chain the user callback after the internal one. * test(pm-adapter): migrate integration tests to ResolvedLayout (SD-2836) The three integration tests in pm-adapter were calling painter.paint(layout, mount) with raw Layout. They now resolveLayout() first and call painter.paint({ resolvedLayout }, mount). All 1794 pm-adapter tests pass. * test(layout-bridge): migrate perf benchmark to ResolvedLayout (SD-2836) The layout-bridge incremental-pipeline performance benchmark called painter.paint(layout, mount) and painter.setData(...). Both are removed by the API collapse. Migrate to resolveLayout() + DomPainterInput so the benchmark continues to exercise the painter under the new contract. * chore(deps): declare @superdoc/layout-resolved as devDep where tests use it (SD-2836) PR1 added test imports of @superdoc/layout-resolved in pm-adapter/src/integration.test.ts and layout-bridge/test/benchmarks/index.ts without declaring it as a devDependency. Both packages now resolve the import locally via pnpm. * fix(super-editor): honor PageDecorationPayload.items contract (SD-2836) When resolveAlignedDecorationItems cannot align items 1:1 with fragments (returns undefined), HeaderFooterSessionManager now bails out with null instead of emitting a payload with items: undefined, which would violate the now-required PageDecorationPayload.items contract from PR2. normalizeDecorationItems narrowed to ResolvedPaintItem[] -> ResolvedPaintItem[]. Also refresh painter-dom README: drop blocks/measures/setData/paint(layout) examples; document the ResolvedLayout-only paint() and the items-aligned- with-fragments invariant on PageDecorationPayload. * [3/3] test(painter): lock ResolvedLayout-only boundary (SD-2836) (#3118) * test(painter): lock ResolvedLayout-only boundary (SD-2836) * chore(painter): drop unused imports and skipped legacy tests (SD-2836) renderer.ts: drop unused Layout, Page, Measure, FlowBlock, ParagraphBorder type imports left over from the migration. index.test.ts: delete the skipped 'decoration item synthesis' describe block (it was protecting the synthesis path that has been removed). * chore(deps): regenerate lockfile after dropping layout-resolved runtime dep (SD-2836) Moves @superdoc/layout-resolved to devDependencies in the lockfile to match package.json, so CI's --frozen-lockfile install matches. Boundary tests still need it under devDependencies. * fix(painter): adapt content-presence gate to ResolvedPage (SD-2836) After rebasing PR1's painter migration onto main, the new column-separator content-presence gate added by SD-2452 (a5ff6ed) reads page.fragments directly. On the new architecture the painter receives a ResolvedPage whose fragments live as page.items[]. Switch the in-region scan to iterate items (every ResolvedPaintItem carries x/y). Also extend the test-only createTestPainter shim to auto-synthesize minimal ParagraphBlock/ParagraphMeasure entries for any layout fragment whose blockId isn't covered by the supplied blocks. Tests that only exercise wrapper-level rendering (column separators, page chrome) can keep using fragAt(...) without boilerplate matching blocks.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stack
What changed
Locks the boundary established by PR1+PR2 so no future change can re-introduce legacy painter inputs or disallowed cross-package imports.
Architecture guard
tests/architecture-boundaries.test.ts(Guard D) forbidpainter-domfrom importingpm-adapter,layout-bridge, orlayout-resolvedat runtime.painters/dom/src/contract-shape.test.tspins the painter's public input/option types toResolvedLayoutviaEqual<X, Y>+expectTypeOf. Any drift in the contract fails the suite.Cleanup
@superdoc/layout-resolvedfrompainters/dom/package.jsonruntimedependencies(kept underdevDependenciesfor the boundary tests).Why this split
The painter is now structurally dumb (PR1) and surface-collapsed (PR2). This PR makes that state ratchet-only: regressing the boundary breaks the build, not just review.
Verification
painter-dom,pm-adapter,layout-bridge,layout-engine,super-editor.Test plan
@superdoc/layout-resolvedinpainter-dom